/****************************************************
	文件：GameObjectPool.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2020/10/4 19:28:23
	功能：GameObject 对象池
*****************************************************/

using System.Collections.Generic;
using UnityEngine;
using HTUtility;

namespace HTFramework.ResKit
{
    public class GameObjectPool
    {
        /// <summary>
        /// 场景中的回收节点（切换场景不销毁）
        /// </summary>
        private Transform mRecycleTrans;
        /// <summary>
        /// GameObject 资源路径
        /// </summary>
        private string mPath;
        /// <summary>
        /// GameObject 资源引用
        /// </summary>
        private GameObject mObj;
        /// <summary>
        /// 池子的最大容量
        /// </summary>
        private int mMaxCount;
        /// <summary>
        /// 从对象池中分配出去，还未回收的对象数量
        /// </summary>
        public int NoRecycleCount { get => mNoRecyclePool.Count; }

        /// <summary>
        /// 池子中可用的 GameObject
        /// </summary>
        private Stack<GameObject> mPool;
        /// <summary>
        /// 已使用，未回收的 GameObject
        /// </summary>
        private List<GameObject> mNoRecyclePool;

        public GameObjectPool(string path, Transform recycleTrans, int maxCount, int initialCount = 1)
        {
            mPath = path;
            mRecycleTrans = recycleTrans;
            mMaxCount = maxCount;
            mPool = new Stack<GameObject>(mMaxCount);
            mNoRecyclePool = new List<GameObject>(mMaxCount);
            mObj = ResLoader.Instance.LoadSync<GameObject>(mPath);
            for (int i = 0; i < initialCount; i++)
            {
                GameObject go = GameObject.Instantiate(mObj);
                go.transform.SetParent(mRecycleTrans);
                mPool.Push(go);
            }
        }

        /// <summary>
        /// 从对象池中生成 GameObject
        /// </summary>
        /// <param name="parent">生成到该父物体下</param>
        /// <param name="createAlthoughtPoolIsEmpty">如果池中已无元素，是否继续生成</param>
        /// <returns></returns>
        public GameObject Spawn(Transform parent, bool createAlthoughtPoolIsEmpty)
        {
            HTLogger.Debug("从 GameObject Pool 中生成 GameObject");
            GameObject ret = null;
            if (mPool.Count > 0)
            {
                ret = mPool.Pop();
                if (System.Object.ReferenceEquals(ret, null))
                {
                    if (createAlthoughtPoolIsEmpty)
                    {
                        ret = GameObject.Instantiate(mObj);
                    }
                }
                mNoRecyclePool.Add(ret);
            }
            else
            {
                if (createAlthoughtPoolIsEmpty)
                {
                    ret = GameObject.Instantiate(mObj);
                    mNoRecyclePool.Add(ret);
                }
            }
            if (System.Object.ReferenceEquals(parent, null) == false)
            {
                ret.transform.SetParent(parent);
            }
            return ret;
        }

        /// <summary>
        /// 回收指定 GameObject
        /// </summary>
        /// <param name="go">待回收的 GameObject</param>
        /// <param name="setToRecycle">是否回收到回收节点</param>
        /// <returns></returns>
        public bool Recycle(GameObject go, bool setToRecycle)
        {
            HTLogger.Debug("回收 GameObject Pool 指定的 GameObject");
            if (System.Object.ReferenceEquals(go, null)) return false;
            if (mMaxCount > 0 && mPool.Count >= mMaxCount)
            {
                mNoRecyclePool.Remove(go);
                GameObject.Destroy(go);
                go = null;
                return false;
            }
            mNoRecyclePool.Remove(go);
            mPool.Push(go);
            if (setToRecycle)
            {
                go.transform.SetParent(mRecycleTrans);
            }
            else
            {
                go.SetActive(false);
            }
            return true;
        }

        /// <summary>
        /// 回收最先从池中取出的 GameObject
        /// </summary>
        /// <param name="setToRecycle">是否回收到回收节点</param>
        public void Recycle(bool setToRecycle)
        {
            HTLogger.Debug("GameObject Pool 回收最先从池中取出的 GameObject");
            GameObject go = mNoRecyclePool[NoRecycleCount - 1];
            mNoRecyclePool.RemoveAt(NoRecycleCount - 1);
            if (mMaxCount > 0 && mPool.Count >= mMaxCount)
            {
                GameObject.Destroy(go);
                go = null;
                return;
            }
            mPool.Push(go);
            if (setToRecycle)
            {
                go.transform.SetParent(mRecycleTrans);
            }
            else
            {
                go.SetActive(false);
            }
        }

        /// <summary>
        /// 回收所有 GameObject
        /// </summary>
        /// <param name="isRecycle">是否回收，若不回收则销毁、卸载资源</param>
        /// <param name="setToRecycle">是否回收到回收节点</param>
        public void RecycleAll(bool isRecycle, bool setToRecycle)
        {
            HTLogger.Debug("回收 GameObject Pool 所有 GameObject");
            if (isRecycle)
            {
                while (NoRecycleCount > 0)
                {
                    Recycle(setToRecycle);
                }
            }
            else
            {
                for (int i = mNoRecyclePool.Count - 1; i >= 0; i--)
                {
                    GameObject.DestroyImmediate(mNoRecyclePool[i]);
                    mNoRecyclePool.RemoveAt(i);
                }
                mPool.Clear();
                HTLogger.Debug("清除 GameObject Pool 的内存资源，Path：{0}", mPath);
                ResLoader.Instance.Release(ResRecorder.Instance.GetResItem(mPath), true);
            }
        }
    }
}
